--[[

Hover Gear "Legacy"
by William "JetBoom" Moodhe
williammoodhe@gmail.com -or- jetboom@noxiousnet.com
http://www.noxiousnet.com/

Further credits displayed by pressing F1 in-game.

]]

AddCSLuaFile("cl_init.lua")
AddCSLuaFile("cl_options.lua")
AddCSLuaFile("cl_rotatetext.lua")
AddCSLuaFile("cl_scoreboard.lua")
AddCSLuaFile("cl_util.lua")
AddCSLuaFile("cl_radio.lua")

AddCSLuaFile("shared.lua")
AddCSLuaFile("sh_colors.lua")
AddCSLuaFile("sh_micro.lua")
AddCSLuaFile("sh_obj_player_extend.lua")
AddCSLuaFile("sh_animations.lua")
AddCSLuaFile("sh_options.lua")

AddCSLuaFile("obj_entity_extend.lua")

AddCSLuaFile("vgui/phelp.lua")
AddCSLuaFile("vgui/poptions.lua")
AddCSLuaFile("vgui/pscoreboard.lua")
AddCSLuaFile("vgui/padminoptions.lua")
AddCSLuaFile("vgui/dplayerpanel.lua")

include("shared.lua")
include("sh_options.lua")

include("sv_obj_player_extend.lua")

include("mapeditor.lua")

if file.Exists(GM.FolderName.."/gamemode/maps/"..game.GetMap()..".lua", "LUA") then
	include("maps/"..game.GetMap()..".lua")
end

function GM:Initialize()
	resource.AddFile("materials/noxctf/sprite_flame.vmt")
	--resource.AddFile("materials/noxctf/sprite_smoke.vmt")

	resource.AddFile("materials/hovergear/speedometer.png")
	resource.AddFile("materials/hovergear/needle.png")
	resource.AddFile("materials/hovergear/health.png")
end

function GM:InitPostEntity()
	physenv.SetGravity(Vector(0, 0, SCALE * -800))
	RunConsoleCommand("sv_gravity", SCALE * 800)
	RunConsoleCommand("sv_removecrazyphysics", 0) --This is to prevent the cars from disappearing.
	RunConsoleCommand("sv_kickerrornum", 0) --This is to prevent retard owners breaking up the game.
	
	self:LoadMapEditorFile()

	local crowdspawns = {}
	local racingspawns = {}

	for _, oldspawn in pairs(ents.FindByClass("gmod_player_start")) do
		if oldspawn.RedTeam then
			table.insert(crowdspawns, oldspawn)
		else
			table.insert(racingspawns, oldspawn)
		end
	end

	table.Add(crowdspawns, ents.FindByClass("info_player_crowd"))
	table.Add(crowdspawns, ents.FindByClass("info_player_rebel"))
	table.Add(racingspawns, ents.FindByClass("info_player_racer"))
	table.Add(racingspawns, ents.FindByClass("info_player_combine"))

	if #crowdspawns == 0 then
		table.Add(crowdspawns, ents.FindByClass("info_player_*"))
		ErrorNoHalt(team.GetName(TEAM_CROWD).." team had no spawns. Using every spawn on the map.\n")
	end
	if #racingspawns == 0 then
		table.Add(racingspawns, ents.FindByClass("info_player_*"))
		ErrorNoHalt(team.GetName(TEAM_RACING).." team had no spawns. Using every spawn on the map.\n")
	end

	team.SetSpawnPoint(TEAM_CROWD, crowdspawns)
	team.SetSpawnPoint(TEAM_RACING, racingspawns)
end

function GM:Think()
	if self.Racing then
		if self.EndRaceTime <= CurTime() then
			gamemode.Call("EndRace")
		else
			if self:ShouldRaceEnd() then
				gamemode.Call("EndRace", team.GetPlayers(TEAM_RACING))
			end
		end
	else
		if self.MinimumRacers <= #self.Queue then
			if self.StartRaceTime then
				if self.StartRaceTime <= CurTime() then
					gamemode.Call("StartRace")
				end
			else
				self.StartRaceTime = CurTime() + self.PreRaceCountdown
				gmod.BroadcastLua("GAMEMODE.StartRaceTime="..tostring(self.StartRaceTime))
			end
		elseif self.StartRaceTime then
			self.StartRaceTime = nil
			gmod.BroadcastLua("GAMEMODE.StartRaceTime=nil")
		end
	end
end

function GM:PlayerInitialSpawn(pl)
	pl.NextSpawnTime = 0
	pl.SpectatedPlayerKey = pl.SpectatedPlayerKey or 0
	pl:SetTeam(TEAM_NOTRACING)
	
	if pl:GetInfoNum("hovergear_first_timer", 0) == 0 then
		pl:SendLua("MakepHelp()")
		RunConsoleCommand("hovergear_first_timer", 1)
	end
end

function GM:PlayerSpawn(pl)
	pl:SetJumpPower(0)
	pl:SetModel(player_manager.TranslatePlayerModel(pl:GetInfo("cl_playermodel")))
	
	local col = pl:GetInfo("cl_playercolor")
	pl:SetPlayerColor(Vector(col))
	
	if pl:Team() == TEAM_RACING then
		local point = pl:Deaths()
		local checktab = pl.CheckpointPositions[point]
		if checktab then
			local hovergear = pl:SpawnHoverGear(checktab[1], checktab[2])
			if hovergear and IsValid(hovergear) then
				hovergear:SetVehicleHealth(10)
			end
		else
			pl:SpawnHoverGear()
		end
	else
		pl:UnSpectate()
	end

	umsg.Start("PlayerSpawn")
		umsg.Entity(pl)
	umsg.End()
end

local LastSpawnPoints = {}
function GM:PlayerSelectSpawn(pl)
	local teamid = pl:Team()
	local tab = team.GetSpawnPoint(teamid)
	local count = #tab
	if count == 0 then return pl end

	local potential = {}

	for _, spawn in pairs(tab) do
		if IsValid(spawn) and not spawn.Disabled and spawn ~= LastSpawnPoints[teamid] and spawn:IsInWorld() then
			local blocked
			for _, ent in pairs(ents.FindInBox(spawn:GetPos() + Vector(-20, -20, 0), spawn:GetPos() + Vector(20, 20, 74))) do
				if ent:IsPlayer() or ent:IsNPC() or string.sub(ent:GetClass(), 1, 5) == "prop_" then
					blocked = true
					break
				end
			end
			if not blocked then
				potential[#potential + 1 ] = spawn
			end
		end
	end

	if #potential > 0 then
		local spawn = potential[math.random(1, #potential)]
		LastSpawnPoints[teamid] = spawn
		return spawn
	end

	return LastSpawnPoints[teamid] or pl
end

function GM:PlayerDeathThink(pl)
	if pl.NextSpawnTime <= CurTime() and (pl:Team() == TEAM_RACING or pl:KeyPressed(IN_ATTACK) or pl:KeyPressed(IN_JUMP)) then
		pl:Spawn()
	end
end

function GM:PlayerDisconnected(pl)
	if IsValid(pl) then
		pl:RemoveHoverGear()
		pl:ExitQueue(true)
		if self.Winner == pl then
			self.Winner = NULL
		end
	end

	if self:ShouldRaceEnd() then
		gamemode.Call("EndRace", team.GetPlayers(TEAM_RACING))
	end
end

function GM:ShowHelp(pl)
	pl:SendLua("MakepHelp()")
end

function GM:ShowTeam(pl)
	if pl:Team() ~= TEAM_RACING and not pl:ExitQueue() then
		pl:EnterQueue()
	end
end

function GM:ShowSpare1(pl)
	pl:SendLua("GAMEMODE:ToggleRadio()")
end

function GM:ShowSpare2(pl)
	pl:SendLua("MakepOptions()")
end

function GM:PlayerChangedTeam(pl, oldteamid, newteamid)
	pl:SetLap(0)
	pl:SetCheckpoint(0)

	if newteamid == TEAM_RACING then
		pl.CheckpointPositions = {}
	else
		pl.CheckpointPositions = nil
	end
end

function GM:StartRace()
	if self.Racing then return end
	
	local IsWinnerPart = false

	for _, ent in pairs(ents.FindByClass(self.VehicleClass)) do
		ent:Remove()
	end

	self.Racing = true
	self.StartRaceTime = CurTime()
	self.EndRaceTime = CurTime() + self.RaceTime

	gamemode.Call("UpdateGameState")

	for i=1, self.MaximumRacers do
		local pl = self.Queue[1]
		table.remove(self.Queue, 1)

		if pl then
			if IsValid(pl) then
				pl:StripWeapons()
				pl:SetTeam(TEAM_RACING)
				pl:Spawn()
				if self.Winner == pl then
					IsWinnerPart = true
				end
			else
				i = i - 1
			end
		end
	end

	gamemode.Call("UpdateQueue")

	if IsValid(self.Winner) and IsWinnerPart then
		PrintMessage(HUD_PRINTCENTER, "The race has started! Beat ".. self.Winner:Name() .." to reign his winning throne.")
	else
		PrintMessage(HUD_PRINTCENTER, "The race has started! Get the winning spot!")
	end
end

function GM:EndRace(winners)
	if not self.Racing then return end

	self.Racing = nil
	self.EndRaceTime = nil
	self.StartRaceTime = nil

	gamemode.Call("UpdateGameState")

	PrintMessage(HUD_PRINTCENTER, "The race has ended!")

	if #team.GetPlayers(TEAM_RACING) == 1 then
		winners = nil
	end

	if winners then
		if type(winners) == "table" then
			if #winners == 0 then
				PrintMessage(HUD_PRINTTALK, "No one won the race!")
			else
				for i, winner in ipairs(winners) do
					PrintMessage(HUD_PRINTTALK, winner:Name().." has won the race!")
					self.Winner = winner
				end
			end
		else
			PrintMessage(HUD_PRINTTALK, winners:Name().." has won the race!")
			self.Winner = winners
		end
	else
		PrintMessage(HUD_PRINTTALK, "No one won the race!")
	end

	for _, pl in pairs(team.GetPlayers(TEAM_RACING)) do
		pl:StripWeapons()
		pl:ExitVehicle()
		pl:SetTeam(TEAM_CROWD)
		pl:Spawn()
	end

	for _, ent in pairs(ents.FindByClass(self.VehicleClass)) do
		ent:Remove()
	end
	
	for _, pl in pairs(player.GetAll()) do
		if pl:GetInfoNum("hovergear_auto_join", 0) == 1 then
			pl:EnterQueue()
		end
	end
end

function GM:ShouldRaceEnd()
	if #team.GetPlayers(TEAM_RACING) < self.MinimumRacers or self.EndRaceTime <= CurTime() then
		return true
	end
end

function GM:UpdateGameState(filter)
	umsg.Start("updgmstate", filter)
		umsg.Bool(self.Racing or false)
		umsg.Float(self.StartRaceTime or 0)
		umsg.Float(self.EndRaceTime or 0)
	umsg.End()
end

function GM:PlayerReady(pl)
	gamemode.Call("UpdateGameState", pl)

	pl:SendLua("GAMEMODE.MaxCheckpoint="..tostring(GAMEMODE.MaxCheckpoint))
end

function GM:PlayerHitCheckpoint(pl, point)
	if point == pl:Deaths() + 1 then
		pl:PrintMessage(HUD_PRINTCENTER, "You hit a checkpoint!")
		pl:SendLua("surface.PlaySound(\"buttons/button14.wav\")")
		pl:SetDeaths(point)

		if pl:InVehicle() then
			pl.CheckpointPositions[point] = {pl:GetVehicle().VehicleParent:GetPos(), pl:GetVehicle().VehicleParent:GetAngles()}
		else
			pl.CheckpointPositions[point] = {pl:GetPos(), pl:GetAngles()}
		end
	elseif point == 0 and pl:Deaths() == self.MaxCheckpoint then
		if pl:InVehicle() then
			pl.CheckpointPositions = {{pl:GetVehicle().VehicleParent:GetPos(), pl:GetVehicle().VehicleParent:GetAngles()}}
		else
			pl.CheckpointPositions = {}
		end

		pl:AddFrags(1)
		if pl:Frags() == self.LapsToWin - 1 then
			PrintMessage(HUD_PRINTCENTER, pl:Name().." is now at the FINAL LAP!")
		else
			PrintMessage(HUD_PRINTCENTER, pl:Name().." is now at LAP "..pl:Frags().." out of "..self.LapsToWin-1)
		end
		pl:SetDeaths(0)
		if pl:Frags() == self.LapsToWin then
			gamemode.Call("EndRace", pl)
		else
			pl:SendLua("surface.PlaySound(\"buttons/button19.wav\")")
		end
	elseif point ~= pl:Deaths() then
		pl:PrintMessage(HUD_PRINTCENTER, "You're going the wrong way, so you have been sent to the last checkpoint.")
		pl:Kill()
	end
end

function GM:GravGunPunt(pl, ent)
	return string.sub(ent:GetClass(), 1, 8) == "vehicle_" and (not ent.Owner or ent.Owner == pl)
end

function GM:PlayerNoClip(pl, on)
	if pl:IsAdmin() then
		if on then
			LogAction("[Admin CMD] <"..pl:SteamID().."> "..pl:Name().." TURNED ON NOCLIP")
		else
			LogAction("[Admin CMD] <"..pl:SteamID().."> "..pl:Name().." TURNED OFF NOCLIP")
		end
		return true
	end

	return false
end

function GM:DoPlayerDeath(pl, attacker, dmginfo)
	pl.NextSpawnTime = CurTime() + 3

	if pl:Team() == TEAM_RACING then
		pl:PrintMessage(HUD_PRINTTALK, "You have been put back at your last checkpoint.")

		if self:ShouldRaceEnd() then
			gamemode.Call("EndRace", team.GetPlayers(TEAM_RACING))
		end
	end
end

function GM:QueueUpdated()
end

function GM:UpdateQueue(filter)
	local tosend = {}
	for i, pl in ipairs(self.Queue) do
		tosend[#tosend + 1] = pl:EntIndex()
	end

	umsg.Start("updateracingqueue", filter)
		umsg.String(table.concat(tosend, ","))
	umsg.End()

	gamemode.Call("QueueUpdated")
end

local vec0 = Vector(0,0,0)
function GM:SetPlayerAnimation(pl)
	if pl:InVehicle() then
		local seq = pl:SelectWeightedSequence(ACT_HL2MP_SIT_FIST)
		if pl:GetSequence() ~= seq then
			pl:ResetSequence(seq)
		end
	else
		local act
		if pl:IsOnGround() then
			local notmoving = vec0 == pl:GetVelocity()
			if pl:Crouching() then
				if notmoving then
					act = ACT_HL2MP_IDLE_CROUCH
				else
					act = ACT_HL2MP_WALK_CROUCH
				end
			elseif notmoving then
				act = ACT_HL2MP_IDLE
			else
				act = ACT_HL2MP_RUN
			end
		else
			act = ACT_HL2MP_JUMP
		end

		local seq = pl:SelectWeightedSequence(pl:Weapon_TranslateActivity(act))
		if seq == -1 then
			seq = pl:SelectWeightedSequence(ACT_HL2MP_JUMP_SLAM)
		end
		if pl:GetSequence() ~= seq then
			pl:ResetSequence(seq)
		end
	end
end